﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Configuration;
using System.Configuration.Provider;
using System.Data.SqlClient;
using System.Data;
using System.Diagnostics;

namespace Auth
{
    public sealed class myRoleProvider : RoleProvider
    {
        #region 变量
        private string applicationName; //应用程序名
        private string connectionString;//连接字符串

        private string eventSource = "myRoleProvider"; //时间来源
        private string eventLog = "Application";
        private string exceptionMessage = "出现异常，请检查Event Log.";

        //false时异常提交给调用者，true时写入日志Event Log
        private bool writeExceptionsToEventLog = false;

        #endregion

        #region 属性获取
        public override string ApplicationName
        {
            get
            {
                return applicationName;
            }
            set
            {
                applicationName = value;
            }
        }
        public bool WriteExceptionsToEventLog
        {
            get { return writeExceptionsToEventLog; }
            set { writeExceptionsToEventLog = value; }
        }

        #endregion

        #region 常用函数重建
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="name"></param>
        /// <param name="config"></param>
        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (name == null || name.Length == 0)
            {
                name = "myRoleProvider";
            }

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "myRoleProvider应用");
            }
            base.Initialize(name, config);
            applicationName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            if (GetConfigValue(config["writeExceptionsToEventLog"], "FALSE") == "TRUE")
            {
                writeExceptionsToEventLog = true;
            }
            ConnectionStringSettings ConnectionStringSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]];

            if ((ConnectionStringSettings == null) || (ConnectionStringSettings.ConnectionString.Trim() == String.Empty))
            {
                throw new ProviderException("连接字符串为空");
            }

            connectionString = ConnectionStringSettings.ConnectionString;
        }
        /// <summary>
        /// 给系列用户增加权限
        /// </summary>
        /// <param name="usernames">用户名</param>
        /// <param name="rolenames">角色</param>
        public override void AddUsersToRoles(string[] usernames, string[] rolenames)
        {
            //检查角色是否存在
            foreach (string rolename in rolenames)
            {
                if (!RoleExists(rolename))
                {
                    throw new ProviderException("角色" + rolename + "不存在.");
                }
            }

            foreach (string username in usernames)
            {
                if (username.Contains(","))
                {
                    throw new ArgumentException("用户名'" + username + "'不能包含逗号.");
                }
            }
            SqlConnection sqlConnection = new SqlConnection(connectionString);
            //更新用户角色信息
            SqlCommand sqlCommand = new SqlCommand("User_AddRole", sqlConnection);

            sqlCommand.CommandType = CommandType.StoredProcedure;
            SqlParameter userParm = sqlCommand.Parameters.Add("@userName", SqlDbType.NVarChar, 255);
            SqlParameter roleParm = sqlCommand.Parameters.Add("@roleName", SqlDbType.NVarChar, 255);

            sqlCommand.Parameters.Add("@applicationName", SqlDbType.NVarChar, 255).Value = applicationName;

            SqlTransaction tran = null;


            try
            {
                sqlConnection.Open();
                tran = sqlConnection.BeginTransaction();
                sqlCommand.Transaction = tran;

                foreach (string username in usernames)
                {
                    foreach (string rolename in rolenames)
                    {
                        userParm.Value = username;
                        roleParm.Value = rolename;
                        sqlCommand.ExecuteNonQuery();
                    }
                }

                tran.Commit();
            }
            catch (SqlException e)
            {
                try
                {
                    tran.Rollback();

                }
                catch { }
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "AddUsersToRoles");
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                sqlConnection.Close();
            }

        }
        /// <summary>
        /// 创建角色
        /// </summary>
        /// <param name="rolename">角色名</param>
        public override void CreateRole(string rolename)
        {
            if (rolename.Contains(","))
            {
                throw new ArgumentException("角色'" + rolename + "'不能包含逗号");
            }

            if (RoleExists(rolename))
            {
                throw new ProviderException("角色" + rolename + "已存在");
            }
            SqlConnection sqlConnection = new SqlConnection(connectionString);
            SqlCommand sqlCommand = new SqlCommand("Role_Ins", sqlConnection);
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlCommand.Parameters.Add("@returnValue", SqlDbType.Int, 0).Direction = ParameterDirection.ReturnValue;
            sqlCommand.Parameters.Add("@rolename", SqlDbType.NVarChar, 255).Value = rolename;
            sqlCommand.Parameters.Add("@applicationName", SqlDbType.NVarChar, 255).Value = applicationName;

            try
            {
                sqlConnection.Open();
                sqlCommand.ExecuteNonQuery();

                if ((int)sqlCommand.Parameters["@returnValue"].Value == 0)
                {

                    //添加成功后操作
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "CreateRole");
                }
                else
                {
                    throw e;
                }

            }
            finally
            {
                sqlConnection.Close();
            }

        }
        /// <summary>
        /// 移除一些用户的一些角色
        /// </summary>
        /// <param name="usernames"></param>
        /// <param name="roleNames"></param>
        public override void RemoveUsersFromRoles(string[] usernames, string[] rolenames)
        {
            foreach (string rolename in rolenames)
            {
                if (!RoleExists(rolename))
                {
                    throw new ProviderException("角色" + rolename + "不存在.");
                }
            }

            foreach (string username in usernames)
            {
                foreach (string rolename in rolenames)
                {
                    if (!IsUserInRole(username, rolename))
                    {
                        throw new ProviderException("用户" + username + "没有" + rolename + "角色");
                    }
                }
            }
            SqlConnection sqlConnection = new SqlConnection(connectionString);
            //更新用户角色信息
            SqlCommand sqlCommand = new SqlCommand("User_DelRole", sqlConnection);

            sqlCommand.CommandType = CommandType.StoredProcedure;
            SqlParameter userParm = sqlCommand.Parameters.Add("@userName", SqlDbType.NVarChar, 255);
            SqlParameter roleParm = sqlCommand.Parameters.Add("@roleName", SqlDbType.NVarChar, 255);

            sqlCommand.Parameters.Add("@applicationName", SqlDbType.NVarChar, 255).Value = applicationName;

            SqlTransaction tran = null;

            try
            {
                sqlConnection.Open();
                tran = sqlConnection.BeginTransaction();
                sqlCommand.Transaction = tran;

                foreach (string username in usernames)
                {
                    foreach (string rolename in rolenames)
                    {
                        userParm.Value = username;
                        roleParm.Value = rolename;
                        sqlCommand.ExecuteNonQuery();
                    }
                }
                tran.Commit();
            }
            catch (SqlException e)
            {
                try
                {
                    tran.Rollback();
                }
                catch { }
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "RemoveUsersFromRoles");
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                sqlConnection.Close();
            }
        }
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {

            throw new NotImplementedException();
        }
        /// <summary>
        /// 判断username是否有rolename角色
        /// </summary>
        /// <param name="username">用户名</param>
        /// <param name="rolename">角色名</param>
        /// <returns></returns>
        public override bool IsUserInRole(string username, string rolename)
        {
            bool userIsInRole = false;

            SqlConnection sqlConnection = new SqlConnection(connectionString);
            SqlCommand sqlCommand = new SqlCommand("User_IsInRole", sqlConnection);
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlCommand.Parameters.Add("@username", SqlDbType.NVarChar, 255).Value = username;
            sqlCommand.Parameters.Add("@rolename", SqlDbType.NVarChar, 255).Value = rolename;
            sqlCommand.Parameters.Add("@applicationName", SqlDbType.NVarChar, 255).Value = applicationName;

            try
            {
                sqlConnection.Open();

                int numRecs = (int)sqlCommand.ExecuteScalar();

                if (numRecs > 0)
                {
                    userIsInRole = true;
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "IsUserInRole");
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                sqlConnection.Close();
            }
            return userIsInRole;
        }
        /// <summary>
        /// 角色是否存在
        /// </summary>
        /// <param name="rolename">角色名</param>
        /// <returns>T/F</returns>
        public override bool RoleExists(string rolename)
        {
            bool exists = false;

            SqlConnection sqlConnection = new SqlConnection(connectionString);
            SqlCommand sqlCommand = new SqlCommand("Role_Sel_ByName", sqlConnection);
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlCommand.Parameters.Add("@rolename", SqlDbType.NVarChar, 255).Value = rolename;
            sqlCommand.Parameters.Add("@applicationName", SqlDbType.NVarChar, 255).Value = applicationName;

            try
            {
                sqlConnection.Open();

                int numRecs = (int)sqlCommand.ExecuteScalar();

                if (numRecs > 0)
                {
                    exists = true;
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "RoleExists");
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                sqlConnection.Close();
            }

            return exists;

        }
        /// <summary>
        /// 在某角色中模糊查找用户
        /// </summary>
        /// <param name="roleName">角色</param>
        /// <param name="usernameToMatch">匹配的用户名</param>
        /// <returns></returns>
        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            string tmpUserNames = "";

            SqlConnection sqlConnection = new SqlConnection(connectionString);
            SqlCommand sqlCommand = new SqlCommand("Users_Sel_ByRoleName", sqlConnection);
            sqlCommand.CommandType = CommandType.StoredProcedure;


            sqlCommand.Parameters.Add("@applicationName", SqlDbType.NVarChar, 255).Value = applicationName;
            sqlCommand.Parameters.Add("@rolename", SqlDbType.NVarChar, 255).Value = roleName;
            sqlCommand.Parameters.Add("@username", SqlDbType.NVarChar, 255).Value = usernameToMatch;

            SqlDataReader sqlDataReader = null;

            try
            {
                sqlConnection.Open();
                sqlDataReader = sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);

                while (sqlDataReader.Read())
                {
                    tmpUserNames += sqlDataReader.GetString(0) + ",";
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "FindUsersInRole");
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (sqlDataReader != null)
                {
                    sqlDataReader.Close();
                }
            }

            if (tmpUserNames.Length > 0)
            {
                // 移除最后的逗号.
                tmpUserNames = tmpUserNames.Substring(0, tmpUserNames.Length - 1);
                return tmpUserNames.Split(',');
            }

            return new string[0];
        }

        /// <summary>
        /// 获取所有角色
        /// </summary>
        /// <returns></returns>
        public override string[] GetAllRoles()
        {
            string tmpRoleNames = "";

            SqlConnection sqlConnection = new SqlConnection(connectionString);
            SqlCommand sqlCommand = new SqlCommand("Roles_Sel", sqlConnection);
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlCommand.Parameters.Add("@applicationName", SqlDbType.NVarChar, 255).Value = applicationName;

            SqlDataReader sqlDataReader = null;

            try
            {
                sqlConnection.Open();

                sqlDataReader = sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);


                while (sqlDataReader.Read())
                {
                    tmpRoleNames += sqlDataReader.GetString(0) + ",";
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetAllRoles");
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (sqlDataReader != null)
                {

                    sqlDataReader.Close();
                }
            }
            //有数据的话，转换成数组
            if (tmpRoleNames.Length > 0)
            {
                // 去掉多余的逗号
                tmpRoleNames = tmpRoleNames.Substring(0, tmpRoleNames.Length - 1);
                return tmpRoleNames.Split(',');
            }

            return new string[0];

        }

        /// <summary>
        /// 获取某用户的所有角色
        /// </summary>
        /// <param name="username">用户名</param>
        /// <returns>角色数组</returns>
        public override string[] GetRolesForUser(string username)
        {
            string tmpRoleNames = "";

            SqlConnection sqlConnection = new SqlConnection(connectionString);
            SqlCommand sqlCommand = new SqlCommand("Roles_Sel_ByUserName", sqlConnection);
            sqlCommand.CommandType = CommandType.StoredProcedure;


            sqlCommand.Parameters.Add("@applicationName", SqlDbType.NVarChar, 255).Value = applicationName;
            sqlCommand.Parameters.Add("@username", SqlDbType.NVarChar, 255).Value = username;

            try
            {
                sqlConnection.Open();

                tmpRoleNames = sqlCommand.ExecuteScalar().ToString();

            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetRolesForUser");
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                sqlConnection.Close();
            }

            if (tmpRoleNames.Length > 0)
            {
                // 去掉逗号
                tmpRoleNames = tmpRoleNames.Trim(','); ;
                return tmpRoleNames.Split(',');
            }

            return new string[0];

        }

        /// <summary>
        /// 有roleName角色的用户
        /// </summary>
        /// <param name="roleName">角色名称</param>
        /// <returns></returns>
        public override string[] GetUsersInRole(string roleName)
        {
            string username = "%";
            return FindUsersInRole(roleName, username);
        }
        #endregion
        #region 有用的函数
        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (String.IsNullOrEmpty(configValue))
            {
                return defaultValue;
            }

            return configValue;
        }

        private void WriteToEventLog(SqlException e, string action)
        {
            EventLog log = new EventLog();
            log.Source = eventSource;
            log.Log = eventLog;

            string message = exceptionMessage + "\n\n";
            message += "Action: " + action + "\n\n";
            message += "Exception: " + e.ToString();

            log.WriteEntry(message);
        }

        #endregion
    }
}